<template>
  <view class="flex flex-col">
    <view v-if="model == 'line'" :style="[{ width: width + 'rpx', paddingTop: '16rpx', paddingBottom: '16rpx' }]" class="flex relative flex flex-col overflow">
      <view class="relative">
        <zh-sheet
          no-level
          :round="props.round"
          :followTheme="false"
          :dark="props.dark"
          :margin="[0, 0]"
          :width="props.width"
          :height="props.height"
          :color="props.bgColor"
          :padding="[0, 0]"
        >
        </zh-sheet>
      </view>
      <view class="absolute l-0 tmRogress overflow" :style="[{ width: activeWidth + 'rpx', top: 16 + 'rpx' }]">
        <zh-sheet
          :round="props.round"
          :followTheme="props.followTheme"
          :dark="props.dark"
          :margin="[0, 0]"
          :linear="props.linear"
          :linearDeep="props.linearDeep"
          :width="activeWidth"
          :height="props.height"
          :color="props.color"
          :padding="[0, 0]"
        >
        </zh-sheet>
      </view>
      <view
        v-if="props.showBar"
        class="absolute l-0 t-0 tmRogress flex flex-col"
        :style="[
          {
            width: activeWidth + 'rpx',
            height: props.height + 32 + 'rpx',
            'align-items': 'flex-end',
            'justify-content': 'center',
          },
        ]"
      >
        <slot>
          <zh-sheet
            :linear="props.linear"
            :linearDeep="props.linearDeep"
            :followTheme="props.followTheme"
            :dark="props.dark"
            :color="props.color"
            :margin="[0, 0]"
            :padding="[12, 4]"
            :round="4"
          >
            <zh-text _class="text-size-xxs" :fontSize="22" :label="props.percent + props.percentSuffix"> </zh-text>
          </zh-sheet>
        </slot>
      </view>
    </view>
    <view
      v-if="model == 'circle'"
      :style="{
        width: `${props.width}rpx`,
        height: `${props.semicircle ? props.width / 2 : props.width}rpx`,
      }"
      class="flex relative flex-col"
    >
      <!-- #ifdef APP-NVUE -->
      <view v-if="showGc">
        <gcanvas
          :id="canvasId"
          :ref="canvasId"
          class="canvas"
          :style="{
            width: `${props.width}rpx`,
            height: `${props.semicircle ? props.width / 2 : props.width}rpx`,
          }"
        >
        </gcanvas>
      </view>
      <!-- #endif -->
      <!-- #ifdef MP-WEIXIN  || MP-QQ -->
      <canvas
        type="2d"
        id="canvasId"
        canvas-id="canvasId"
        class="canvas"
        :style="{
          width: `${props.width}rpx`,
          height: `${props.semicircle ? props.width / 2 : width}rpx`,
        }"
      ></canvas>
      <!-- #endif -->
      <!-- #ifdef MP-ALIPAY -->
      <canvas
        type="2d"
        :id="canvasId"
        :canvas-id="canvasId"
        class="canvas"
        :style="{
          width: `${props.width}rpx`,
          height: `${props.semicircle ? props.width / 2 : width}rpx`,
        }"
      ></canvas>
      <!-- #endif -->
      <!-- #ifndef MP-WEIXIN || MP-ALIPAY || MP-QQ || APP-NVUE -->
      <canvas
        :id="canvasId"
        :canvas-id="canvasId"
        class="canvas"
        :style="{
          width: `${props.width}rpx`,
          height: `${props.semicircle ? props.width / 2 : props.width}rpx`,
        }"
      ></canvas>
      <!-- #endif -->
      <!-- #ifndef APP-NVUE -->
      <cover-view
        :style="[
          {
            width: `${props.width}rpx`,
            height: `${props.semicircle ? props.width / 2 : props.width}rpx`,
          },
          props.semicircle && props.semicircleFlip ? { 'justify-content': 'flex-start', 'align-items': 'center' } : '',
          props.semicircle && !props.semicircleFlip ? { 'justify-content': 'flex-end', 'align-items': 'center' } : '',
        ]"
        class="relative absolute l-0 t-0 flex flex-col"
        :class="[!props.semicircle ? 'flex-center' : '']"
      >
        <cover-view v-if="props.showBar" :style="[{ fontSize: props.fontSize + 'rpx', color: isDark ? darkcolor : txtcolor }]">
          <slot name="title">
            {{ props.percent + props.percentSuffix }}
          </slot>
        </cover-view>
      </cover-view>
      <!-- #endif -->
      <!-- #ifdef APP-NVUE -->
      <cover-view
        :style="[
          {
            width: `${props.width}rpx`,
            height: `${props.semicircle ? props.width / 2 : props.width}rpx`,
          },
          props.semicircle && props.semicircleFlip ? { 'justify-content': 'flex-start', 'align-items': 'center' } : '',
          props.semicircle && !props.semicircleFlip ? { 'justify-content': 'flex-end', 'align-items': 'center' } : '',
        ]"
        class="relative absolute l-0 t-0 flex flex-col"
        :class="[!props.semicircle ? 'flex-center' : '']"
      >
        <slot name="title">
          <zh-text
            v-if="props.showBar"
            :color="props.color"
            :followTheme="props.followTheme"
            :dark="props.dark"
            :fontSize="props.fontSize"
            :label="props.percent + props.percentSuffix"
          >
          </zh-text>
        </slot>
      </cover-view>
      <!-- #endif -->
    </view>
  </view>
</template>

<script lang="ts" setup>
/**
 * 进度条
 * @description 进度条，圆形进度条，在不同的平台使用2d或者webgl方法，使得性能更加强劲。NVUE中貌似是uniapp的插件bug无法实现渐变绘制。
 * Nvue中需要在manifest.json中设置canvas模块，才能打包详见：https://github.com/dcloudio/NvueCanvasDemo
 */
import { cssstyle, systemConfigState, colorThemeType } from '@/tool/lib/interface';
import { custom_props, computedDark, computedTheme } from '@/tool/lib/minxs';
import { getCurrentInstance, computed, ref, onBeforeMount, onUnmounted, onMounted, nextTick, watch, PropType } from 'vue';

import tool from '@/tool/theme/theme';
// #ifdef APP-NVUE
import { enable, WeexBridge } from '@/tool/gcanvas/index.js';
// #endif
import { getCanvas } from '@/tool/function/getCanvas';
import * as TWEEN from '@/tool/lib/tween.min.js';
import systemConfigStore from '@/store/modules/systemConfigStore';
const store = systemConfigStore();
const emits = defineEmits(['update:percent', 'change']);
const proxy = getCurrentInstance()?.proxy ?? null;
const props = defineProps({
  ...custom_props,
  model: {
    type: String as PropType<'line' | 'circle'>,
    default: 'line', //line,circle
  },
  //model==circle,是否是半圆。
  semicircle: {
    type: [Boolean, String],
    default: false,
  },
  //model==circle有效,半圆正常是在上方。如果反转就在下方。
  semicircleFlip: {
    type: [Boolean],
    default: false,
  },
  //model==circle有效
  fontSize: {
    type: [Number, String],
    default: 28,
  },
  //进度 百分比数值，不带%号。也可以使用v-model:percent
  percent: {
    type: Number,
    default: 0,
  },
  //数值后缀。默认为%
  percentSuffix: {
    type: String,
    default: '%',
  },
  width: {
    type: Number,
    default: 120,
  },
  height: {
    type: Number,
    default: 6,
  },
  bgColor: {
    type: String,
    default: 'grey-4',
  },
  color: {
    type: String,
    default: 'primary',
  },
  //是否跟随全局主题的变换而变换
  followTheme: {
    type: [Boolean, String],
    default: true,
  },
  //暗黑
  dark: {
    type: [Boolean],
    default: false,
  },
  linear: {
    type: [String],
    default: '', //left:右->左，right:左->右。top:下->上，bottom:上->下。
  },
  // 渐变的亮浅
  linearDeep: {
    type: [String],
    default: 'light', //light,dark,accent亮系渐变和深色渐变。
  },
  round: {
    type: [Number, String],
    default: 3,
  },
  //显示数值点
  showBar: {
    type: [Boolean, String],
    default: false,
  },
  disabled: {
    type: Boolean,
    default: false,
  },
});
const canvasId = ref('canvasId');
// #ifndef MP-WEIXIN || MP-QQ
canvasId.value = 'zh' + String(new Date().getTime());
// #endif
let ctx: UniApp.CanvasContext;
let reqId: number;
let tween: any;
const shadow_pr = computed(() => props.shadow * 4);
// 设置响应式全局组件库配置表。
const tmcfg = computed<systemConfigState>(() => store);
//是否暗黑模式。
const isDark = computed(() => computedDark(props, tmcfg.value));
//计算主题
const tmcomputed = computed<cssstyle>(() => computedTheme(props, isDark.value, tmcfg.value));
const showGc = ref(false);
const sys = uni.getSystemInfoSync();
let isAndroid = false;
// #ifdef APP-NVUE
if (sys.osName == 'android') {
  isAndroid = true;
  showGc.value = false;
}
// #endif
const _bgColor = computed(() => computedTheme({ ...props, color: props.bgColor, followTheme: false, linear: '' }, isDark.value, tmcfg.value).backgroundColor);
const txtcolor = tool.getColor(props.color).value;
const darkcolor = tmcomputed.value.backgroundColor;
const activeWidth = computed(() => {
  let pr = props.percent >= 100 ? 100 : props.percent;
  let w = Math.floor((pr / 100) * props.width);
  w = w >= props.width ? props.width : w;
  return w;
});
const percent_rp = computed(() => {
  let pr = props.percent >= 100 ? 100 : props.percent;
  return pr;
});
watch(
  () => props.percent,
  (newval, oldval) => {
    if (props.disabled) return;
    tween = new TWEEN.Tween({ o: oldval })
      .to({ o: newval }, 240)
      .easing(TWEEN.Easing.Linear.None)
      .onUpdate((e: any) => {
        draw(e.o);
      })
      .start();
    emits('update:percent', newval);
    emits('change', newval);
  }
);
onBeforeMount(() => {
  clearTimeout(reqId);
  reqFun();
});
onMounted(() => {
  nextTick(function () {
    let delay = 10;
    // #ifdef APP-NVUE
    if (isAndroid) {
      delay = 250;
    } else {
      delay = 100;
    }
    // #endif
    // #ifdef MP
    delay = 60;
    // #endif
    // #ifdef APP-VUE
    delay = 30;
    // #endif
    // #ifdef APP-NVUE
    setTimeout(function () {
      showGc.value = true;
      setTimeout(() => {
        getCanvas(proxy, canvasId.value, uni.upx2px(props.width), uni.upx2px(props.width))
          .then((e) => {
            ctx = e.ctx;
            tween = new TWEEN.Tween({ o: 0 })
              .to({ o: props.percent }, 240)
              .easing(TWEEN.Easing.Linear.None)
              .onUpdate((e: any) => {
                draw(e.o);
              })
              .start();
          })
          .catch((error) => {
            console.error(error);
          });
      }, delay);
    }, 200);
    // #endif

    // #ifndef APP-NVUE
    setTimeout(() => {
      getCanvas(proxy, canvasId.value, uni.upx2px(props.width), uni.upx2px(props.width))
        .then((e) => {
          ctx = e.ctx;
          tween = new TWEEN.Tween({ o: 0 })
            .to({ o: props.percent }, 240)
            .easing(TWEEN.Easing.Linear.None)
            .onUpdate((e: any) => {
              draw(e.o);
            })
            .start();
        })
        .catch((error) => {
          console.error(error);
        });
    }, delay);
    // #endif
  });
});
onUnmounted(() => uni.$tm.u.cancelAnimationFrame(reqId));
function reqFun() {
  TWEEN.update();
  reqId = uni.$tm.u.requestAnimationFrame(reqFun);
}
function draw(bl: number = 0) {
  if (!ctx) return;
  let c: any = tmcomputed.value;
  const width = uni.upx2px(props.width);
  let x = width / 2;
  let y = width / 2;
  const borderWidth = uni.upx2px(props.height);
  const radius = x - borderWidth;
  const Pi = Math.PI / 180;
  let startAngle = -Pi * 90;
  let endAngle = Math.PI * 2;
  if (props.semicircle) {
    startAngle = -Pi * 90 * 2;
    endAngle = 0;
    if (props.semicircleFlip) {
      startAngle = Pi * 180;
      endAngle = 0;
    }
  }
  //圆环背景色
  let bgColor = _bgColor.value || '#f5f5f5';
  //圆环进度条的颜色
  let activeColor = tool.getColor(props.color).csscolor || '#ff0000';

  ctx.clearRect(0, 0, width, width);
  //先绘制背景圆;
  ctx.lineWidth = borderWidth;
  ctx.strokeStyle = bgColor;
  ctx.lineCap = 'round';
  ctx.beginPath();
  if (props.semicircle) {
    // 半圆
    if (props.semicircleFlip) {
      y = borderWidth / 2;
      ctx.arc(x, y, radius, 0, Pi * 180, false);
    } else {
      y = y - borderWidth / 2;
      ctx.arc(x, y, radius, Pi * 180, 0, false);
    }
  } else {
    ctx.arc(x, y, radius, -Math.PI / 2, 1.5 * Math.PI);
  }
  ctx.stroke();

  // 绘制圆形进度环
  const progress = Number((bl / 100).toFixed(6)); // 进度比例，0-1之间的数值
  ctx.beginPath();
  if (props.semicircle) {
    // 半圆
    if (props.semicircleFlip) {
      ctx.arc(x, y, radius, startAngle * (1 - progress), startAngle, false);
    } else {
      ctx.arc(x, y, radius, startAngle, startAngle * (1 - progress), false);
    }
  } else {
    if (props.semicircleFlip) {
      ctx.arc(x, y, radius, startAngle + endAngle * (1 - progress), startAngle + endAngle, false);
    } else {
      ctx.arc(x, y, radius, startAngle, startAngle + endAngle * progress, false);
    }
  }
  if (ctx.setLineWidth) {
    ctx.setLineWidth(borderWidth);
  } else {
    ctx.lineWidth = borderWidth;
  }

  // #ifndef APP-NVUE
  //如果是渐变
  if (props.linear) {
    let gradient: any = ctx.createLinearGradient(borderWidth / 2, borderWidth / 2, x * 2 - borderWidth, y * 2 - borderWidth);
    if (props.semicircle) {
      gradient = ctx.createLinearGradient(borderWidth / 2, borderWidth / 2, x * 2 - borderWidth, y);
    }
    gradient.addColorStop(0, c.gradientColor[0]);
    gradient.addColorStop(1, c.gradientColor[1]);
    // ctx.setStrokeStyle(gradient)
    if (ctx.setStrokeStyle) {
      ctx.setStrokeStyle(gradient);
      ctx.setShadow(c.gradientColor[0]);
    } else {
      ctx.strokeStyle = gradient;
      ctx.shadowColor = c.gradientColor[0];
    }
  } else {
    if (ctx.setStrokeStyle) {
      ctx.setStrokeStyle(activeColor);
      ctx.setShadow(activeColor);
    } else {
      ctx.strokeStyle = activeColor;
      ctx.shadowColor = activeColor;
    }
  }

  // #endif
  // #ifdef APP-NVUE
  if (ctx.setStrokeStyle) {
    ctx.setStrokeStyle(activeColor);
  } else {
    ctx.strokeStyle = activeColor;
  }
  // #endif

  ctx.stroke();
  if (ctx?.draw) {
    ctx.draw();
  }
}
</script>
<style>
.tmRogress {
  transition-duration: 0.34s;
  transition-timing-function: linear;
  transition-property: width;
}

/* #ifndef APP-NVUE */
cover-view {
  background: transparent;
}

/* #endif */
</style>
